package com.xuhuan.demo.filerepeat.util;

import com.xuhuan.demo.file.FileMd5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.*;

/**
 * @Author huan.xu
 * @Date 2023/7/8 14:48
 * @Description 重复文件工具类
 */
@Slf4j
public class FileRepeatUtil {

    private FileRepeatUtil() {
    }

    /**
     * 内容重复文件的map
     */
    private static Map<String, Set<String>> CONTENT_FILE_REPEAT_MAP = new HashMap<>(100);
    /**
     * 内容没有重复文件的map
     */
    private static Map<String, String> CONTENT_FILE_NOT_REPEAT_MAP = new HashMap<>(300);

    /**
     * 名称重复文件的map
     */
    private static Map<String, Set<String>> NAME_FILE_REPEAT_MAP = new HashMap<>(100);

    /**
     * 名称没有重复文件的map
     */
    private static Map<String, String> NAME_FILE_NOT_REPEAT_MAP = new HashMap<>(300);
    /**
     * 所有文件map
     */
    private static Map<String, Object> NAME_FILE_LIST = new HashMap<>(300);

    /**
     * 初始化文件信息到map中，包含重复的map和不重复的map
     * 递归获取所有的文件，计算每个文件的md5值，md5值为map的key，路径为value
     *
     * @param dirPath
     * @param ignoreChildDirPath
     */
    private static void initFileMd5Data(String dirPath, String[] ignoreChildDirPath) throws Exception {
        File dir = new File(dirPath);
        if (!dir.exists()) {
            throw new RuntimeException("目录不存在");
        }
        if (!dir.isDirectory()) {
            throw new RuntimeException("目录不正常");
        }
        recursionContentHandle(dir, ignoreChildDirPath);
    }

    /**
     * 递归处理
     *
     * @param file
     * @param ignoreChildDirPath
     */
    private static void recursionContentHandle(File file, String[] ignoreChildDirPath) throws Exception {
        if (file.isFile()) {
            String md5HashCode = FileMd5Util.md5HashCode(file);
            String filePath = CONTENT_FILE_NOT_REPEAT_MAP.get(md5HashCode);
            if (StringUtils.isNotBlank(filePath)) {
                //重复了，放入存重复数据的地方
                Set<String> filePathSet = CONTENT_FILE_REPEAT_MAP.get(md5HashCode);
                if (CollectionUtils.isEmpty(filePathSet)) {
                    filePathSet = new HashSet<>();
                    CONTENT_FILE_REPEAT_MAP.put(md5HashCode, filePathSet);
                }
                filePathSet.add(filePath);
                filePathSet.add(file.getPath());

            } else {
                CONTENT_FILE_NOT_REPEAT_MAP.put(md5HashCode, file.getPath());
            }
        } else if (file.isDirectory()) {
            File[] dirFileArray = file.listFiles();
            for (File dirFile : dirFileArray) {
                boolean ignoreFlag = Arrays.stream(ignoreChildDirPath).anyMatch(s -> s.equals(dirFile.getPath()));
                if (!ignoreFlag) {
                    recursionContentHandle(dirFile, ignoreChildDirPath);
                }
            }
        }
    }

    /**
     * 获取目录下文件内容重复的文件
     *
     * @param dirPath
     * @param ignoreChildDirPath 忽略的子目录
     * @return
     */
    public static Map<String, Set<String>> getDirRepeatContentFile(String dirPath, String[] ignoreChildDirPath) throws Exception {
        log.info("开始查找目录下重复内容文件,目录:", dirPath);
        initFileMd5Data(dirPath, ignoreChildDirPath);
        return CONTENT_FILE_REPEAT_MAP;
    }

    /**
     * 初始化文件信息到map中，包含重复的map和不重复的map
     * 递归获取所有的文件，文件名为map的key，路径为value
     *
     * @param dirPath
     */
    private static void initFileData(String dirPath) throws Exception {
        File dir = new File(dirPath);
        if (!dir.exists()) {
            throw new RuntimeException("目录不存在");
        }
        if (!dir.isDirectory()) {
            throw new RuntimeException("目录不正常");
        }
        recursionNameHandle(dir);
    }

    /**
     * 递归处理
     *
     * @param file
     */
    private static void recursionNameHandle(File file) throws Exception {
        if (file.isFile()) {
            //处理汇总信息
            Integer totalFileSize = (Integer) NAME_FILE_LIST.get("totalFileSize");
            if (totalFileSize == null) {
                totalFileSize = 0;
            }
            NAME_FILE_LIST.put("totalFileSize", totalFileSize + 1);

            List<String> fileList = (List<String>) NAME_FILE_LIST.get("fileList");
            if (CollectionUtils.isEmpty(fileList)) {
                fileList = new ArrayList<>();
                NAME_FILE_LIST.put("fileList", fileList);
            }
            fileList.add(file.getPath());

            //处理重复和不重复的数据
            String fileName = file.getName();
            String filePath = NAME_FILE_NOT_REPEAT_MAP.get(fileName);
            if (StringUtils.isNotBlank(filePath)) {
                Set<String> filePathSet = NAME_FILE_REPEAT_MAP.get(fileName);
                if (CollectionUtils.isEmpty(filePathSet)) {
                    filePathSet = new HashSet<>();
                    NAME_FILE_REPEAT_MAP.put(fileName, filePathSet);
                }
                filePathSet.add(filePath);
                filePathSet.add(file.getPath());

            } else {
                NAME_FILE_NOT_REPEAT_MAP.put(fileName, file.getPath());
            }
        } else if (file.isDirectory()) {
            File[] dirFileArray = file.listFiles();
            for (File dirFile : dirFileArray) {
                recursionNameHandle(dirFile);
            }
        }
    }

    /**
     * 获取目录下所有文件
     *
     * @param dirPath
     * @return
     * @throws Exception
     */
    public static Map<String, Object> getDirFile(String dirPath) throws Exception {
        log.info("开始查找目录下所有文件,目录:", dirPath);
        initFileData(dirPath);
        return NAME_FILE_LIST;
    }

    /**
     * 获取目录下名称重复的文件
     *
     * @param dirPath
     * @return
     * @throws Exception
     */
    public static Map<String, Set<String>> getDirRepeatNameFile(String dirPath) throws Exception {
        log.info("开始查找目录下名称重复的文件,目录:", dirPath);
        initFileData(dirPath);
        return NAME_FILE_REPEAT_MAP;
    }
}
